// player.c
// --------
// CS 181
// 
// Example player for the final project

#include "../shared/socklib.h"
#include "../shared/game_util.h"
#include <string>
#include <stdlib.h>
#include <iostream>
#include "neural-net.h"

using namespace std;


#include "data-reader.h"
#include "neural-net.h"
#include <assert.h>
#include <cstdlib>
#include <cstdio>
#include <sstream>
#include <iostream>
#include <map>

using std::cout;
using std::endl;
using std::map;
using std::stringstream;

void InitInputs(NeuralNetwork* network) {
  for (int i = 0; i < 6 * 6; ++i) {
    Node* node = new Node();
    network->AddNode(node, NeuralNetwork::INPUT);
  }
}

/**
 * Simple network without any hidden layers.
 */
void InitSingleLayerNetwork(NeuralNetwork* network) {
  // Initialize the network
  // 6 * 6 inputs_
  InitInputs(network);
  for (int i = 0; i < 2; ++i) {
    Node* node = new Node();
    for (size_t j = 0; j < network->inputs_.size(); ++j) {
      node->AddInput(network->inputs_[j], NULL, network);
    }
    network->AddNode(node, NeuralNetwork::OUTPUT);
  }
}

class PlantNetwork {
 public:
  PlantNetwork(const string& type) {
    InitSingleLayerNetwork(&network_);
  }

  int GetNetworkLabel() {
    // Indeterminate if there is more than one output that is a one.
    /*int label = 0;
    double best_score = 0.0;
    for (size_t i = 0; i < network_.outputs_.size(); ++i) {
      Node* node = network_.outputs_[i];
      if (i == 0 || node->transformed_value_ > best_score) {
        label = i;
        best_score = node->transformed_value_;
      }
    }    
    return label;*/
    
    double diff = network_.outputs_[1]->transformed_value_ - network_.outputs_[0]->transformed_value_;
    printf("diff: %f ", diff);
    
    return (diff>-0.25)?1:0;
  }

  Input Convert(double image[IMAGE_SIZE][IMAGE_SIZE]) {
    Input input;
    for (int i = 0; i < 6; ++i) {
      for (int j = 0; j < 6; ++j) {
        input.values.push_back(image[i][j]);
      }
    }
    return input;
  }

  int Classify(double image[IMAGE_SIZE][IMAGE_SIZE]) {
    Input input = Convert(image);
    network_.FeedForward(input);
    return GetNetworkLabel();
  }

  NeuralNetwork network_;
};


// Function Prototypes
int parse_input(int argc, char **argv, int &port);
void print_usage();
void print_plant_image(int sock);
void print_instructions();

int main(int argc, char **argv) {
  int port, sock;
  string input;

  // Parse the command line
  if(parse_input(argc, argv, port) < 0) {
    print_usage();
	return 1;
  }
  
  // Open a socket to the game server
  // Assumes that the game server is listening on the local host
  sock = open_client_sock("localhost", port);
  if(sock < 0) {
    printf("Error connecting to game server on port %d\n", port);
	return 1;
  }
  /*
  // Print status messages
  printf("Connected to game server at port %d\n\n", port);

  // Print life settings
  printf("Starting Life: %d\nPlant Bonus: %d\nPlant Penalty: %d\n\n",
  get_starting_life(sock), get_plant_bonus(sock), get_plant_penalty(sock));
  */
  
  printf("Reading weights... ");
  string weights_file = "weights_plant";
  PlantNetwork plantClassifier("simple");
  plantClassifier.network_.InitFromWeights(DataReader::ReadWeights(weights_file));
  printf("done!\n");
  
  int c_noPlant = 0;
  int c_unknownPlant = 0;
  int c_obs = 0;
  int c_nutri = 0;
  int c_poison = 0;
  int c_nutriE = 0;
  int c_poisonE = 0;
  int obs[201][201]; // hack could segfault -- watch out
  for(int i=0; i<201; i++) {
    for(int j=0; j<201; j++) {
      obs[i][j]=-1;
    }
  }
  
  // Try to move in a spiral
  int spiralWidth=1;
  int dir=0;
  //int stepsInDir=0;
  int movedInDir=0;
  
  // Loop as long as the player is alive
  while(alive(sock)) {
    // Print the current round
    //printf("Round: %d\n\n", get_round(sock));

    // Print game info
    //printf("Player is at (%d, %d) with life %d\n",
    //getx(sock), gety(sock), get_life(sock));
      
    switch(get_plant_type(sock)) {
      case NO_PLANT:
        //printf("No plant at in this location\n");
        //printf(".");
        c_noPlant++;
        obs[getx(sock)+100][gety(sock)+100] = NO_PLANT;
      break;

      case UNKNOWN_PLANT:
        //printf("Plant at this location has not been eaten\n");
        //printf("\n");
        //printf("P");
        //c_unknownPlant++;
        
        if(obs[getx(sock)+100][gety(sock)+100] == -1) {
          
          printf("%d %d %d : ", c_obs, c_nutri, c_poison);
          
          int image[IMAGE_SIZE][IMAGE_SIZE];
          double imageAvg[IMAGE_SIZE][IMAGE_SIZE];
          for(int i=0; i<IMAGE_SIZE; i++) {
            for(int j=0; j<IMAGE_SIZE; j++) {
              imageAvg[i][j] = 0.0;
            }
          }
          
          int MAX_OBS = 1;
          
          for(int numObs=0; numObs<MAX_OBS; numObs++) {
            if(get_plant_image(sock, image) < 0) {
              printf("Error getting image\n");
            }            
            for(int i=0; i<IMAGE_SIZE; i++) {
              for(int j=0; j<IMAGE_SIZE; j++) {
                imageAvg[i][j] += image[i][j];
              }
            }
          }
          
          for(int i=0; i<IMAGE_SIZE; i++) {
            for(int j=0; j<IMAGE_SIZE; j++) {
              imageAvg[i][j] /= MAX_OBS;
            }
          }
          
          c_obs++;
          int predicted_class;
          predicted_class = plantClassifier.Classify(imageAvg);
          //printf("%d", predicted_class);
          
          if(predicted_class==1){
            printf("Looks yummy...");
            
            switch(eat_plant(sock)) {
              case EAT_NUTRITIOUS_PLANT:
                printf("YAY!");
                c_nutri++;
                obs[getx(sock)+100][gety(sock)+100] = NUTRITIOUS_PLANT;
              break;
              
              case EAT_POISONOUS_PLANT:
                printf("EWW!");
                c_poison++;
                obs[getx(sock)+100][gety(sock)+100] = POISONOUS_PLANT;
              break;
            }
          } else {
            printf("Looks gross. No eating.");
            obs[getx(sock)+100][gety(sock)+100] = UNKNOWN_PLANT;
          }
        } else {
          printf("Seen before... skipped observation");
        }
        
        printf("\n");
        
        //eat_plant(sock);
      break;
      
      case NUTRITIOUS_PLANT:
        //printf("Plant at this location has been eaten\n");
        //printf("Plant was nutritious\n");
        //printf("\n");
        //printf("n");
        c_nutriE++;
      break;

      case POISONOUS_PLANT:
        //printf("Plant at this location has been eaten\n");
        //printf("Plant was poisonous\n");
        //printf("\n");
        //printf("p");
        c_poisonE++;
      break;
    }
    
    // move
    if(dir==0) {
      move_right(sock);
      movedInDir=getx(sock);
    } else if(dir==1) {
      move_up(sock);
      movedInDir=gety(sock);
    } else if(dir==2) {
      move_left(sock);
      movedInDir=-getx(sock);
    } else {
      move_down(sock);
      movedInDir=-gety(sock);
    }
    
    if(movedInDir>=spiralWidth){
      dir = (dir+1)%4;
      if(dir==0)
        spiralWidth+=1;
    }
    
    fflush(0);
  }

  // Tell the server to end the game
  end_game(sock);

  // Print a message for the user
  //printf("Player is dead: GAME OVER!\n\n");
  //printf("No plant: %d\nNutritious: %d\nPoisonous: %d\nNutrious Already Eaten: %d\nPoisonous Already Eaten: %d\n", c_noPlant, c_nutri, c_poison, c_nutriE, c_poisonE);
}

// parse_input
// -----------
// Parse the command line input
// Returns 0 on success; -1 on error
int parse_input(int argc, char **argv, int &port) {
  if(argc != 2) {
    return -1;
  } 
  
  port = atoi(argv[1]);
    return 0;
  }

// print_usage
// -----------
// Print proper command line usage
void print_usage() {
  printf("usage: player port\n");
}

// print_plant_image
// -----------------
// Print a plant image
void print_plant_image(int sock) {
  int row, col;
  int image[IMAGE_SIZE][IMAGE_SIZE];
  
  if(get_plant_image(sock, image) < 0) {
    printf("Error getting image\n");
  
  } else {
    //printf("Plant image:\n");
    for(row = 0; row < IMAGE_SIZE; row++) {
      for(col = 0; col < IMAGE_SIZE; col++) {
	    printf("%3d", image[row][col]);
	  }
   
      printf("\n");
	}
  }
}
